﻿using System;
using System.IO;
using System.Security;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
using System.Drawing;
using Microsoft.Build.BuildEngine;
using System.Text;
using System.Globalization;

namespace SPALM
{
  public class ULSFormatLogger : Logger
  {
    // Fields
    private bool append;
    private Encoding encoding;
    private static readonly char[] fileLoggerParameterDelimiters = new char[] { ';' };
    private static readonly char[] fileLoggerParameterValueSplitCharacter = new char[] { '=' };
    private StreamWriter fileWriter;
    private string logFileName;
    private bool usingSpecificVerbosity;
    private LoggerVerbosity verbosity;

    // Methods
    public ULSFormatLogger()
    {
      this.logFileName = "msbuild.log";
      this.encoding = Encoding.Default;
    }

    internal void ParseParameters()
    {
      if (this.Parameters != null)
      {
        string[] strArray = this.Parameters.Split(fileLoggerParameterDelimiters);
        for (int i = 0; i < strArray.Length; i++)
        {
          if (strArray[i].Length > 0)
          {
            string[] strArray2 = strArray[i].Split(fileLoggerParameterValueSplitCharacter);
            if (strArray2.Length > 1)
            {
              this.ApplyParameter(strArray2[0], strArray2[1]);
            }
            else
            {
              this.ApplyParameter(strArray2[0], null);
            }
          }
        }
      }
    }

    private void ApplyParameter(string name, string value)
    {
      if (name.ToUpper() == "LOGFILE")
      {
        this.logFileName = value;
      }
      else if (name.ToUpper() == "APPEND")
      {
        this.append = Boolean.Parse(value);
      }
      else if (name.ToUpper() == "VERBOSITY")
      {
        this.verbosity = (LoggerVerbosity)Enum.Parse(typeof(LoggerVerbosity), value, true);
      }
    }

    public override void Initialize(IEventSource eventSource)
    {
      this.ParseParameters();

      eventSource.BuildStarted += new BuildStartedEventHandler(eventSource_BuildStarted);
      eventSource.BuildFinished += new BuildFinishedEventHandler(eventSource_BuildFinished);
      eventSource.ProjectStarted += new ProjectStartedEventHandler(eventSource_ProjectStarted);
      eventSource.ProjectFinished += new ProjectFinishedEventHandler(eventSource_ProjectFinished);
      eventSource.TargetStarted += new TargetStartedEventHandler(eventSource_TargetStarted);
      eventSource.TargetFinished += new TargetFinishedEventHandler(eventSource_TargetFinished);
      eventSource.TaskStarted += new TaskStartedEventHandler(eventSource_TaskStarted);
      eventSource.TaskFinished += new TaskFinishedEventHandler(eventSource_TaskFinished);
      eventSource.ErrorRaised += new BuildErrorEventHandler(eventSource_ErrorRaised);
      eventSource.WarningRaised += new BuildWarningEventHandler(eventSource_WarningRaised);
      eventSource.MessageRaised += new BuildMessageEventHandler(eventSource_MessageRaised);
      eventSource.CustomEventRaised += new CustomBuildEventHandler(eventSource_CustomEventRaised);

      if (this.usingSpecificVerbosity)
      {
        base.Verbosity = this.verbosity;
      }
      try
      {
        this.fileWriter = new StreamWriter(this.logFileName, this.append, this.encoding);
        this.fileWriter.AutoFlush = true;
      }
      catch (Exception)
      {
        throw new LoggerException("InvalidFileLoggerFile");
      }
    }

    void eventSource_CustomEventRaised(object sender, CustomBuildEventArgs e)
    {
      Write(e);
    }

    void eventSource_MessageRaised(object sender, BuildMessageEventArgs e)
    {
      Write(e);
    }

    void eventSource_WarningRaised(object sender, BuildWarningEventArgs e)
    {
      Write(e);
    }

    void eventSource_ErrorRaised(object sender, BuildErrorEventArgs e)
    {
      Write(e);
    }

    private string currentTarget = "MSBuild";
    void eventSource_TaskStarted(object sender, TaskStartedEventArgs e)
    {
      currentTask = e.TaskName;
      //do not write current task
      //Write(e);
    }

    private string currentTask = "MSBuild";
    void eventSource_TaskFinished(object sender, TaskFinishedEventArgs e)
    {
      currentTask = "";
      //do not write for finished task
      //Write(e);
    }

    void eventSource_TargetFinished(object sender, TargetFinishedEventArgs e)
    {
      currentTarget = "MSBuild";
    }

    void eventSource_TargetStarted(object sender, TargetStartedEventArgs e)
    {
      if (e.TargetName != null)
      {
        currentTarget = e.TargetName;
      }
    }

    void eventSource_ProjectFinished(object sender, ProjectFinishedEventArgs e)
    {
      Write(e);
    }

    void eventSource_ProjectStarted(object sender, ProjectStartedEventArgs e)
    {
      Write(e);
    }

    void eventSource_BuildFinished(object sender, BuildFinishedEventArgs e)
    {
      Write(e);
    }

    void eventSource_BuildStarted(object sender, BuildStartedEventArgs e)
    {
      WriteHeader();
      Write(e);
    }

    public override void Shutdown()
    {
      try
      {
      }
      finally
      {
        this.fileWriter.Close();
      }
    }

    private void WriteHeader()
    {
      WriteFormattedLine("Timestamp", "Process", "TID", "Area", "Category", "EventID", "Level", "Message");
    }

    private void WriteFormattedLine(string Timestamp, string Process, string TID, string Area, string Category, string EventId, string Level, string Message)
    {
      //Timestamp              	Process                                 	TID   	Area                          	Category                      	EventID	Level     	Message
      if (Area.Length > 30)
      {
        Area = Area.Substring(0, 30);
      }
      if (Category.Length > 30)
      {
        Category = Category.Substring(0, 30);
      }
      //formatiere text
      string completeline = "";
      completeline += Timestamp.PadRight(23, ' ') + "\t";
      completeline += Process.PadRight(40, ' ') + "\t";
      completeline += TID.PadRight(6, ' ') + "\t";
      completeline += Area.PadRight(30, ' ') + "\t";
      completeline += Category.PadRight(30, ' ') + "\t";
      completeline += EventId.PadRight(4, ' ') + "\t";
      completeline += Level.PadRight(8, ' ') + "\t";
      completeline += Message;

      try
      {
        this.fileWriter.WriteLine(completeline);
      }
      catch (Exception)
      {
        throw new LoggerException("InvalidFileLoggerFile");
      }
    }

    private void Write(BuildEventArgs e)
    {
      if (string.IsNullOrEmpty(e.Message))
      {
        return;
      }
      if (string.IsNullOrEmpty(e.Message.Trim()))
      {
        return;
      }

      CultureInfo ci = CultureInfo.InvariantCulture;
      string datetime = e.Timestamp.ToString("MM/dd/yyyy HH:mm:ss.ff", ci);
      string process = "msbuild.exe";
      string TID = e.ThreadId.ToString();
      string Area = currentTarget;
      string Category = "Message";
      string EventId = "";
      string Level = "Low";
      string Message = e.Message;

      if (e is BuildErrorEventArgs)
      {
        Level = "Critical";
        Category = "Error";
      }
      else if (e is BuildWarningEventArgs)
      {
        Level = "High";
        Category = "Warning";
      }
      else if (e is BuildFinishedEventArgs)
      {
        Level = "High";
        Category = "BuildFinished";
      }
      else if(e is BuildMessageEventArgs)
      {
        Level = (e as BuildMessageEventArgs).Importance.ToString();
        if(Level == "Normal")
        {
          Level = "Medium";
        }
        if(Level == "Low")
        {
          Level = "Verbose";
        }
        Category = "Message";
      }
      else if (e is BuildStartedEventArgs)
      {
        Level = "High";        
        Category = "BuildStarted";        
      }
      else if (e is ProjectStartedEventArgs)
      {
        Level = "High";
        Message = "Project started " + (e as ProjectStartedEventArgs).ProjectFile;
        Category = "ProjectStarted";

      }
      else if (e is ProjectFinishedEventArgs)
      {
        Level = "High";
        Message = "Project finished " + (e as ProjectFinishedEventArgs).ProjectFile;
        Category = "ProjectFinished";
      }

      if (currentTask != "")
      {
        Category = currentTask;
      }

      Category = e.SenderName;

      if (Verbosity == LoggerVerbosity.Quiet)
      {
        //write nothing
        return;
      }
      else if(Verbosity == LoggerVerbosity.Minimal)
      {
        //only high messages
        if(Level == "Verbose")
        {
          return;
        }
        if(Level == "Medium")
        {
          return;
        }
      }
      else if(Verbosity == LoggerVerbosity.Normal)
      {
        //no verbose
        if(Level == "Verbose")
        {
          return;
        }
      }
      else if(Verbosity == LoggerVerbosity.Detailed)
      {
      }
      else if(Verbosity == LoggerVerbosity.Diagnostic)
      {
      }

      //write each line separately
      StringReader reader = new StringReader(Message);
      while (true)
      {
        string aLine = reader.ReadLine();
        if (aLine == null)
        {          
          break;
        }
        WriteFormattedLine(datetime, process, TID, Area, Category, EventId, Level, aLine);
      }
    }
  }
}

